昨天我們看過了預設程式碼內 routing()
的實作
routing {
get("/") {
call.respondText("Hello World!")
}
}
看完 routing()
的實作之後,下一步我們來看 get()
函數的實作。
get()
函數可以在 Ktor 框架內幫我們定義一個接收 GET 請求的路由,實作如下
/**
* Builds a route to match `GET` requests with the specified [path].
* @see [Application.routing]
*/
@KtorDsl
public fun RoutingBuilder.get(path: String, body: RoutingHandler): RoutingBuilder {
return route(path, HttpMethod.Get) { handle(body) }
}
粗略地看,可以看出這邊做的事情就是使用 route
函數,搭配 HttpMethod.Get
,來建立一個 GET 的路由。
不過細心的讀者可能會發現:body
應該要是一個函數,才能允許程式內使用 {}
定義內容。這邊的寫法 RoutingHandler
,好像是一個介面或者類別一樣?
我們來看看 RoutingHandler
的實作:
public typealias RoutingHandler = suspend RoutingContext.() -> Unit
我們會發現如前面所說,body
確實是一個函數沒錯,不過為了讓程式看起來更加好懂,這邊利用了 Kotlin 的 typealias
關鍵字,將 RoutingHandler
定義成了 suspend RoutingContext.() -> Unit
的同義詞。
另外我們也會發現,這個函數標記了 suspend
關鍵字,所以這會是一個可以用 Coroutine 暫停的函數。
下一段,我們來看 route()
的實作
@KtorDsl
public fun RoutingBuilder.route(path: String, method: HttpMethod, build: RoutingBuilder.() -> Unit): RoutingBuilder {
val selector = HttpMethodRouteSelector(method)
return createRouteFromPath(path).createChild(selector).apply(build)
}
根據 HttpMethodRouteSelector
的命名,我們可以猜這是用來篩選請求是否跟設定的 HTTP Method 一致。
我們來看看,實際上是不是我們所想的這樣:
public data class HttpMethodRouteSelector(
val method: HttpMethod
) : RouteSelector() {
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {
if (context.call.request.httpMethod == method) {
return RouteSelectorEvaluation.Constant
}
return RouteSelectorEvaluation.FailedMethod
}
override fun toString(): String = "(method:${method.value})"
}
可能有點出乎意料,HttpMethodRouteSelector
竟然是一個 data class
!這邊利用了 data class
幫忙實作 setter 的部分,所以我們只需要實作判斷的 evaluate()
。如果請求的 method 和設置的 method 相同,這邊會回傳 RouteSelectorEvaluation.Constant
,定義如下:
/**
* Route evaluation succeeded for a constant value.
*/
public val Constant: RouteSelectorEvaluation = RouteSelectorEvaluation.Success(RouteSelectorEvaluation.qualityConstant)
如果比對不相同,則改回傳 RouteSelectorEvaluation.FailedMethod
。
看過了 HttpMethodRouteSelector
,我們明天再來看看
return createRouteFromPath(path).createChild(selector).apply(build)
這一段程式碼,是怎麼處理我們輸入的路徑的